/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.staf.SnmpTopologyExplorer.V2;

import java.util.HashSet;
import java.util.TreeMap;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Set;
import java.util.List;
import java.util.Iterator;

import org.snmp4j.smi.OID;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.VariableBinding;

import ru.staf.SnmpTopologyExplorer.*;

/**
 *
 * @author aezhov
 */
public class Switch {

    public static final String MAC_OID = ".1.3.6.1.2.1.17.4.3.1.2";
    public static final String MAC_OID2 = "1.3.6.1.2.1.17.7.1.2.2.1.2";

    String name = "[UNKNOWN]";
    HashSet<String> finalSet = new HashSet<String>();
    TreeMap<Integer, HashSet<String>> fdb = new TreeMap<Integer, HashSet<String>>();
    TreeMap<Integer, ArrayList<Switch>> potLinks =
                new TreeMap<Integer, ArrayList<Switch>>();
    HashMap<Switch, Integer> nbTable = new HashMap<Switch, Integer>();
    TreeMap<Integer, Switch> linkTable = new TreeMap<Integer, Switch>();
    TreeMap<Integer, Boolean> isConsistent = new TreeMap<Integer, Boolean>();
    
    GenericAgent snmpAgent = null;
    NetScanner netScanner = null;


    public Switch(Switch requestor, String name, Set<String> macs) {
        this.name = name;
        this.netScanner = requestor.netScanner;
        int pnum = 2;

        Iterator<String> it = macs.iterator();
        while (it.hasNext()) {
            HashSet<String> mac = new HashSet<String>();
            mac.add(it.next());
            Integer oNum = new Integer(pnum++);
            fdb.put(oNum, mac);
            isConsistent.put(oNum, Boolean.TRUE);
        }

        ArrayList<Switch> req = new ArrayList<Switch>();
        req.add(requestor);
        potLinks.put(new Integer(1), req);
    }

    public Switch(String name, GenericAgent snmpAgent, NetScanner netScanInfo)
            throws ProcessException, java.io.IOException {
        this.name = name;
        this.snmpAgent = snmpAgent;
        this.netScanner = netScanInfo;

        VariableBinding[][] variables = new VariableBinding[2][];
        variables[0] = snmpAgent.walk(MAC_OID);
        variables[1] = snmpAgent.walk(MAC_OID2);
        int[] offsets = {11, 14};

        for (int i = 0; i < 2; i++) {
            for (int k = 0; k < variables[i].length; k++) {
                String MAC = null;

                VariableBinding vb = variables[i][k];
                int[] oidParts = vb.getOid().getValue();
                MAC = toMacString(oidParts, offsets[i]);
                Integer32 var = (Integer32) vb.getVariable();
                Integer port = new Integer(var.toInt());
                if (port.intValue() == 0)
                    continue;
                
                HashSet<String> macs = null;
                if (!fdb.containsKey(port)) {
                    macs = new HashSet<String>();
                    macs.add(MAC);
                    fdb.put(port, macs);
                } else {
                    macs = fdb.get(port);
                    macs.add(MAC);
                }
            }
        }

        // Make the finalSet for me
        Set<Integer> pNums = fdb.keySet();
        Iterator<Integer> pit = pNums.iterator();
        while (pit.hasNext()) {
            Integer port = pit.next();
            HashSet<String> macs = fdb.get(port);
            
            if (macs.size() == 1) {
                finalSet.addAll(macs);
            }
        }

        
        System.out.println("=== SWITCH " + name + " final set size == " + finalSet.size());
    }

    public boolean haveOneOfThem(Set<String> macs) {
        Iterator<String> mac = macs.iterator();
        while (mac.hasNext()) {
            String addr = mac.next();
            if (finalSet.contains(addr)) {
                System.out.print(" (Switch " + name + " identified by " + addr +") ");
                return true;
            }
        }

        return false;
    }
    
    public void calculateCrossSwitchRelations(TopologyModeler modeler,
            List<Switch> neighbours)
    {
        int nNb = neighbours.size();

        Set<Integer> pNums = fdb.keySet();
        Iterator<Integer> pnit = pNums.iterator();
        System.out.println("=== SWITCH " + name);

        while (pnit.hasNext()) {
            Integer pNum = pnit.next();
            ArrayList<Switch> value = new ArrayList<Switch>();
            HashSet<String> macs = fdb.get(pNum);
            if (macs.size() == 1)
                continue;
            
            System.out.print("    Port " + pNum + ": ");

            for (int i = 0; i < nNb; i++) {
                Switch nb = neighbours.get(i);
                if (nb.equals(this))
                    continue;
                if (nb.haveOneOfThem(macs)) {
                    System.out.print(nb.name + ", ");
                    value.add(nb);
                    nbTable.put(nb, pNum);
                } 
            }

            if (value.isEmpty()) {
                System.out.print(" found trunk to unknown switch");
                value.add(modeler.registerAnonymousSwitch(this, macs));
            }

            potLinks.put(pNum, value);
            System.out.println("");
        }
    }

    public void calculateNearestNeighbours() {
        Set<Integer> keys = potLinks.keySet();
        Iterator<Integer> pNums = keys.iterator();
        while (pNums.hasNext()) {
            Integer pNum = pNums.next();
            List<Switch> members = potLinks.get(pNum);
            if (members.size() > 0) {
                System.out.println("=== SW " + name + " processing trunk port " + pNum);
                Switch lastSwitch = null;
                int lastLength = 4096;

                for (int i = 0; i < members.size(); i++) {
                    Switch nb = members.get(i);
                    System.out.println("    process neighbour " + nb.name);
                    int len = nb.getNumOfNodesBeforeMe(this, members);
                    if (len < lastLength) {
                        lastLength = len;
                        lastSwitch = nb;
                    }
                }

                linkTable.put(pNum, lastSwitch);
            }
        }
    }

    public void checkLinkConsistency() {
        System.out.println("=== Check switch " + name);
        Set<Integer> ports = linkTable.keySet();
        Iterator<Integer> pNums = ports.iterator();
        
        while (pNums.hasNext()) {
            Integer pNum = pNums.next();
            Switch nb = linkTable.get(pNum);
            System.out.println("    Checking port " + pNum);
            System.out.println("        Seems to be connected to switch " + nb.name);
            int nbPort = nb.doYouHaveLinkToMe(this);
            if (nbPort == 0) {
                System.out.println("        --- INCONSISTENCY ! Neighbour doesn't see me");
                isConsistent.put(pNum, Boolean.FALSE);
            } else {
                isConsistent.put(pNum, Boolean.TRUE);
            }
        }
    }

    public int doYouHaveLinkToMe(Switch requestor) {
        Set<Integer> ports = linkTable.keySet();
        Iterator<Integer> pit = ports.iterator();
        while (pit.hasNext()) {
            Integer pnumber = pit.next();
            Switch sw = linkTable.get(pnumber);
            if (sw.equals(requestor))
                return pnumber.intValue();
        }

        return 0;
    }
    
    public int getNumOfNodesBeforeMe(Switch me, List<Switch> path) {
        Integer mePnum = nbTable.get(me);
        int counter = 0;

        if (mePnum == null) {
            System.out.println("        Undefined switch " + me.name +
                    " for switch " + name);
            return -1;
        }

        Iterator<Switch> pIt = path.iterator();

        while (pIt.hasNext()) {
            Switch sw = pIt.next();
            if (sw.equals(this)) {
                System.out.println("        Ignoring switch " + sw.name);
                continue;
            }

            Integer swpNum = nbTable.get(sw);
            if (swpNum == null) {
                System.out.println("        Attafuck! " + sw.name);
                continue;
            }

            if (swpNum.equals(mePnum))
                counter++;
        }
        
        System.out.println("        Length == " + counter);

        return counter;
    }
    
    public String toMacString(int[] oidParts, int index) {
        String result = "";

        for (int k = 0; k < 6; k++) {
            String delim = k == 5 ? "" : ":";
            String prefix = oidParts[k + index] < 16 ? "0" : "";
            result = result + prefix + Integer.toHexString(oidParts[k + index]) + delim;
        }

        return result;
    }
}
